<!-- eslint-disable -->
<template>
  <div class="basic-form submit">
    <el-form :model="props.model" :ref="handleRef" :inline="inline" :label-width="labelWidth">
      <el-row>
        <el-col :span="formItem.col ?? 24" v-for="formItem in schemas" :key="formItem.field">
          <h3 :class="formItem.className" v-if="formItem.type === 'title'">{{ formItem.label }}</h3>
          <el-form-item
            :label="formItem.label"
            :label-width="formItem.labelWidth ?? labelWidth"
            :prop="formItem.field"
            v-else
            :rules="getFormItemRules(formItem.rules)"
          >
            <slot v-if="slots[formItem.field]" :name="formItem.field"></slot>
            <el-input
              v-else-if="
                formItem.type === 'text' ||
                formItem.type === 'password' ||
                formItem.type === 'textarea'
              "
              v-model.trim="props.model[formItem.field]"
              :type="formItem.type"
              :clearable="formItem.clearable ?? true"
              :class="formItem.className"
              :minlength="formItem.minlength ?? 0"
              :maxlength="formItem.maxlength ?? 142"
              :show-word-limit="formItem.showWordLimit"
              :rows="formItem.rows ?? 2"
              :show-password="(formItem.showPassword ?? true) && formItem.type === 'password'"
              :disabled="formItem.disabled"
              :readonly="formItem.readonly"
              :placeholder="getPlaceholder(formItem.type, formItem.placeholder)"
              @change="handleFormItemChange(formItem)"
              @input="
            ($event:string) => {
              handleValueFormatter($event, formItem)
            }
          "
            />
            <el-radio-group
                v-else-if="formItem.type === 'radio'"
                v-model="props.model[formItem.field]"
            >
              <div v-if="formItem.checkedType === 'default'">
                <el-radio
                    v-for="item in formItem.options"
                    :key="item.value"
                    :label="item.value"
                    :border="formItem.checkedBorder ?? false"
                    :disabled="formItem.disabled ?? false"
                >
                  {{ item.label }}
                </el-radio>
              </div>
              <div v-else-if="formItem.checkedType === 'button'">
                <el-radio-button
                    v-for="item in formItem.options"
                    :key="item.value"
                    :label="item.value"
                    :border="formItem.checkedBorder ?? false"
                    :disabled="formItem.disabled ?? false"
                >
                  {{ item.label }}
                </el-radio-button>
              </div>
            </el-radio-group>
            <el-checkbox-group
                v-else-if="formItem.type === 'checkbox'"
                v-model="props.model[formItem.field]"
                :disabled="formItem.disabled ?? false"
            >
              <div v-if="formItem.checkedType === 'default'">
                <el-checkbox
                    v-for="item in formItem.options"
                    :key="item.value"
                    :label="item.value"
                    :border="formItem.checkedBorder ?? false"
                >
                  {{ item.label }}
                </el-checkbox>
              </div>
              <div v-else-if="formItem.checkedType === 'button'">
                <el-checkbox-button
                    v-for="item in formItem.options"
                    :key="item.value"
                    :label="item.value"
                >
                  {{ item.label }}
                </el-checkbox-button>
              </div>
            </el-checkbox-group>
            <el-color-picker
                v-else-if="formItem.type === 'color'"
                v-model="props.model[formItem.field]"
                :disabled="formItem.disabled ?? false"
                :show-alpha="formItem.showAlpha??false"
                :color-format="formItem.colorFormat??'hex'"
                :predefine="formItem.predefine??['#ff4500', '#ffd700', '#1e90ff', '#c71585']"
                :popper-class="formItem.popperClass"
                :class="formItem.className"
            />
            <el-input-number
                v-else-if="formItem.type === 'number'"
                v-model.trim="props.model[formItem.field]"
                :class="formItem.className"
                :min="formItem.min || -Infinity"
                :max="formItem.max || Infinity"
                :step="formItem.step || 1"
                :step-strictly="formItem.stepStrictly ?? false"
                :precision="formItem.precision ?? 0"
                :disabled="formItem.disabled ?? false"
                :readonly="formItem.readonly ?? false"
                :controls="formItem.controls ?? true"
                :controls-position="formItem.controlsPosition ?? ''"
                :placeholder="getPlaceholder(formItem.type, formItem.placeholder)"
                @change="handleFormItemChange(formItem)"
            />
            <el-select
              v-else-if="formItem.type === 'select'"
              v-model="props.model[formItem.field]"
              :class="formItem.className"
              :popper-class="formItem.popperClass"
              :clearable="formItem.clearable ?? true"
              :filterable="formItem.filterable??true"
              :teleported="formItem.teleported ?? true"
              :disabled="formItem.disabled"
              :readonly="formItem.readonly ?? false"
              :multiple="formItem.multiple"
              :multiple-limit="formItem.multipleLimit ?? 0"
              :collapse-tags="formItem.collapseTags ?? true"
              :collapse-tags-tooltip="formItem.collapseTagsTooltip ?? true"
              :placeholder="getPlaceholder(formItem.type, formItem.placeholder)"
              @focus="handleFormItemFocus(formItem)"
            >
              <el-option
                v-for="selectItem in formItem.options ?? []"
                :key="
                  selectItem[
                    formItem.optionsFrom === 'dict' ? 'dictValue' : formItem.props?.value || 'value'
                  ]
                "
                :label="
                  selectItem[
                    formItem.optionsFrom === 'dict' ? 'dictLabel' : formItem.props?.label || 'label'
                  ]
                "
                :value="
                  selectItem[
                    formItem.optionsFrom === 'dict' ? 'dictValue' : formItem.props?.value || 'value'
                  ]
                "
              />
            </el-select>
            <el-cascader
              v-else-if="formItem.type === 'cascader'"
              v-model="props.model[formItem.field]"
              :class="formItem.className"
              :popper-class="formItem.popperClass"
              :options="formItem.options ?? []"
              :props="formItem.props ?? {}"
              :clearable="formItem.clearable ?? true"
              filterable
              :disabled="formItem.disabled"
              :show-all-levels="formItem.showAllLevels ?? true"
              :teleported="formItem.teleported ?? true"
              :collapse-tags="formItem.collapseTags ?? true"
              :collapse-tags-tooltip="formItem.collapseTagsTooltip ?? true"
              :separator="formItem.separator ?? '/'"
              :placeholder="getPlaceholder(formItem.type, formItem.placeholder)"
              @focus="handleFormItemFocus(formItem)"
            />
            <el-tree-select
              v-else-if="formItem.type === 'treeSelect'"
              v-model="props.model[formItem.field]"
              :class="formItem.className"
              :popper-class="formItem.popperClass"
              :data="formItem.options ?? []"
              :props="formItem.props ?? {}"
              :check-strictly="formItem.props?.checkStrictly ?? false"
              :clearable="formItem.clearable ?? true"
              filterable
              :default-expand-all="formItem.defaultExpandAll ?? false"
              :teleported="formItem.teleported ?? true"
              :disabled="formItem.disabled"
              :multiple="formItem.multiple"
              :multiple-limit="formItem.multipleLimit || 0"
              :collapse-tags="formItem.collapseTags ?? true"
              :collapse-tags-tooltip="formItem.collapseTagsTooltip ?? true"
              :placeholder="getPlaceholder(formItem.type, formItem.placeholder)"
              @focus="handleFormItemFocus(formItem)"
            />
            <el-date-picker
              v-else-if="singleDateTypeList.includes(formItem.type)"
              v-model="props.model[formItem.field]"
              :class="formItem.className"
              :popper-class="formItem.popperClass"
              :type="formItem.type"
              :clearable="formItem.clearable ?? true"
              :format="
                formItem.format ?? 'YYYY-MM-DD' + (formItem.type === 'datetime' ? ' HH:mm:ss' : '')
              "
              :value-format="
                formItem.valueFormat ??
                'YYYY-MM-DD' + (formItem.type === 'datetime' ? ' HH:mm:ss' : '')
              "
              :disabled="formItem.disabled"
              :readonly="formItem.readonly"
              :disabled-date="(time:Date) => getDisabledDate(time, formItem)"
              :teleported="formItem.teleported ?? true"
              :placeholder="getPlaceholder(formItem.type, formItem.placeholder)"
            />
            <el-date-picker
              v-else-if="rangeDateTypeList.includes(formItem.type)"
              v-model="props.model[formItem.field]"
              :class="formItem.className"
              :popper-class="formItem.popperClass"
              :type="formItem.type"
              :clearable="formItem.clearable ?? true"
              :format="
                formItem.format ||
                'YYYY-MM-DD' + (formItem.type === 'datetimerange' ? ' HH:mm:ss' : '')
              "
              :value-format="
                formItem.valueFormat ||
                'YYYY-MM-DD' + (formItem.type === 'datetimerange' ? ' HH:mm:ss' : '')
              "
              :disabled="formItem.disabled"
              :readonly="formItem.readonly"
              :disabled-date="(time:Date) => getDisabledDate(time, formItem)"
              :teleported="formItem.teleported ?? true"
              :default-time="defaultTime"
              :unlink-panels="formItem.unlinkPanels ?? true"
              :start-placeholder="
                getPlaceholder(formItem.type, formItem.startPlaceholder ?? '开始时间')
              "
              :end-placeholder="
                getPlaceholder(formItem.type, formItem.endPlaceholder ?? '结束时间')
              "
            />
            <Upload
              v-else-if="formItem.type === 'upload'"
              v-model="props.model[formItem.field]"
              :upload-text="formItem.uploadText ?? '选择文件'"
              :hide-file="formItem.hideFile ?? false"
              :drag="formItem.drag ?? false"
              :list-type="formItem.listType ?? 'picture-card'"
              :headers="formItem.headers ?? {}"
              :data="formItem.data ?? {}"
              :accept="formItem.accept ?? 'image/*'"
              :limit="formItem.limit ?? 1"
              :max-size="formItem.maxSize ?? 2"
            ></Upload>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
  </div>
</template>
<script setup name="SubmitForm" lang="ts">
import axios from 'axios'
import { useSlots, ref, computed, watch } from 'vue'
import type { FormInstance } from 'element-plus'
import { wrapperToken } from '@/utils'
import { parseRules } from '@/utils/rules'
import { Upload } from '@/components/Upload'

const slots = useSlots()

const emit = defineEmits(['change', 'search', 'reset'])

const props = withDefaults(
  defineProps<{
    model: Recordable
    schemas: SubmitFormSchema[]
    inline?: boolean
    labelWidth?: string
  }>(),
  {
    labelWidth: '120px'
  }
)
const formRef = ref<FormInstance>()
const handleRef = el => {
  if (el) {
    formRef.value = el
  }
}
// 校验参数
const validateForm = (): Promise<void> => {
  return new Promise((resolve, reject) => {
    formRef.value?.validate(valid => {
      if (valid) {
        resolve()
      }
      reject('数据校验未通过')
    })
  })
}
// 清空校验
const resetFields = (): void => {
  formRef.value?.resetFields()
}
// 后台请求ip
const requestIp = computed(() => import.meta.env.VITE_BASE_API)

const singleDateTypeList = ['date', 'datetime', 'week', 'month', 'year', 'dates']
const rangeDateTypeList = ['daterange', 'datetimerange', 'monthrange', 'yearrange']

const defaultTime: Date[] = [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)]
// 获取表单项实际rule配置
const getFormItemRules = rules => {
  if (typeof rules === 'string') {
    return parseRules(rules)
  }
  return rules
}
// 获取"占位符"
const getPlaceholder = (placeholderType: string, placeholderText: string | undefined) => {
  if (['text', 'textarea', 'number', 'password'].includes(placeholderType)) {
    return placeholderText || '请输入'
  }
  return placeholderText || '请选择'
}
// 获取"禁用日期"
const getDisabledDate = (time: Date, formItem: SubmitFormSchema): boolean => {
  if (formItem.disabledDateType) {
    const disabledDateTypeMap = {
      before: time.getTime() < Date.now() - 8.64e7,
      after: time.getTime() > Date.now()
    }
    return disabledDateTypeMap[formItem.disabledDateType]
  }
  return false
}

const handleFormItemChange = (formItem: SubmitFormSchema): void => {
  emit('change', formItem)
}
const handleFormItemFocus = async (formItem: SubmitFormSchema) => {
  // 如果options有值，说明已经获取过了，不需要再次获取
  if (formItem.optionState && formItem.options && formItem.options.length > 0) {
    return false
  }
  switch (formItem.optionsFrom) {
    // 模拟通过字典dict获取
    case 'dict':
      // formItem.options = dictStore.getDictInfo[formItem.dictType ?? 'empty']?.options || []
      formItem.options = []
      break
    // 通过前端提供api方法获取
    case 'frontendApi':
      try {
        if (formItem.optionsApi) {
          const { code, data } = await formItem.optionsApi()
          if (code === 0 || code === 200) {
            formItem.options = data
          }
        }
      } catch (e) {
        console.log(e)
      }
      break
    // 通过后端提供api url与方式 获取
    case 'backendApi':
      axios[formItem.optionsApiType ?? 'post'](requestIp.value + formItem.optionsApi, {
        headers: {
          [import.meta.env.VITE_TOKEN_STR]: wrapperToken.getToken()
        }
      })
        .then(res => {
          const { code, data } = res.data
          if (code === 200 || code === 0) {
            formItem.options = data
          }
        })
        .catch(err => {
          console.log(err)
        })
      break
    default:
      break
  }
  // 已经获取过options
  formItem.optionState = true
  return false
}

const handleValueFormatter = (val: string, formItem: SubmitFormSchema): void => {
  if (formItem.formatter && formItem.type !== 'number') {
    // eslint-disable-next-line vue/no-mutating-props
    props.model[formItem.field] = val.replace(formItem.formatter, '')
  }
}
watch(
  () => props.schemas,
  (newVal, oldVal) => {
    // 只有首次加载时，如果设置optionsImmediate,则直接调取接口数据赋值给options
    if (!oldVal) {
      const immediateList = newVal.filter(
        item => item.optionsImmediate && ['select', 'cascader', 'treeSelect'].includes(item.type)
      )
      immediateList.forEach(formItem => {
        handleFormItemFocus(formItem)
      })
    }
  },
  { immediate: true, deep: true }
)

defineExpose({ validateForm, resetFields })
</script>
<style lang="scss">
.basic-form.submit {
  .el-select,
  .el-cascader {
    width: 100%;
  }
}
</style>
